package sync

import (
	"sync/atomic"

	"gddgame.cc/galaxy/asteroid"
	"gddgame.cc/galaxy/asteroid/cluster"
	"gddgame.cc/galaxy/binocle"
	"gddgame.cc/galaxy/binocle/module/socket"
	"gddgame.cc/galaxy/common/feature/orbit"
	"gddgame.cc/galaxy/common/feature/orbit/sync_protocol"
	"gddgame.cc/galaxy/satellite/formula/comet"
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/def"
	"gddgame.cc/galaxy/utils/serialize"
	"github.com/vmihailenco/msgpack"
)

type SingleModeService struct {
	def.Logger
	binocle.Service

	Block *BlockService
}

type singleInstance struct {
	MaxNum     uint32
	Block      *blockInstance
	DataStream socket.StreamRequest
}

// 用于balance的replicate同步
func (i *singleInstance) Marshal() ([]byte, error) {
	return msgpack.Marshal(i)
}
func (i *singleInstance) Unmarshal(b []byte) error {
	return msgpack.Unmarshal(b, &i)
}
func (i *singleInstance) Join(member *memberInstance, node cluster.Node) {

}
func (i *singleInstance) Leave(member *memberInstance, node cluster.Node) {
	// 所有用户移除后，关闭
	if i.Block.Empty() {
		node.Debugf("Sync Leave->Close:%s", node.Token())
		_ = node.Close()
		i.Block.connecting = false
	}
}

func (i *singleInstance) UniqueId() uint32 {
	atomic.AddUint32(&i.MaxNum, 1)
	return i.MaxNum
}

func (i *singleInstance) Data(data ...interface{}) {
	if i.DataStream != nil {
		i.DataStream.Push(data)
	}
}

func (i *singleInstance) Location(openId string, location []byte) {

}

func (service *SingleModeService) ID() string {
	return "syncSingle"
}
func (service *SingleModeService) Master(node cluster.Node) {

}
func (service *SingleModeService) Init(node cluster.Node) {
	block := orbit.LocationCM.NodeBlock("")
	instance := &singleInstance{
		MaxNum: 0,
		Block:  NewBlockInstance(node.Token(), block, service, true),
	}

	node.SetConfig(cluster.ThreadSafe, true)
	node.SetInstance(instance)
	service.Block.InitService(node, instance.Block, instance)
}
func (service *SingleModeService) InitClient(client *cluster.Client) {
	service.Block.InitClient(client)

	client.Filter(asteroid.DefaultFilter, func(message asteroid.Message, member def.Member) {
		socket := member.(*comet.Socket)
		d, _ := message.Payload()
		socket.Send(comet.DE(utils.ToByte(message.GetEvent())), d)
	})
}

func (service *SingleModeService) CloseInstance(node cluster.Node) {
	instance := node.Instance().(*singleInstance)
	service.Block.CloseInstance(node, instance.Block)
}

func (service *SingleModeService) CloseClient(client *cluster.Client) {

}

func (service *SingleModeService) NeedBlock() bool {
	return false
}

func (service *SingleModeService) GetWithLocation(c *Client, container cluster.Container, locationByte []byte) *cluster.Client {
	return c.CurrentClient
}
func (service *SingleModeService) GetWithBlock(c *Client, container cluster.Container, block *orbit.BlockLocation) *cluster.Client {
	//fmt.Println(service.ID(), area, key)
	return container.Balance().GenerateClient(service.ID(), service.GetNodeId(c.Area, block))
}

func (service *SingleModeService) GetWithNodeId(container cluster.Container, nodeId string) *cluster.Client {
	return container.Balance().GenerateClient(service.ID(), nodeId)
}

func (service *SingleModeService) GetNodeId(area string, block *orbit.BlockLocation) string {
	return area
}

func (service *SingleModeService) UpdateLocation(c *Client, location *orbit.Location) {

}

func (service *SingleModeService) Dispatch(c *Client, container cluster.Container, event comet.DE, packet comet.Packet) {
	switch event {
	case sync_protocol.Queue:
		// [hash int64, type byte, localVersion, xx]
		// Info: [location []byte, level byte, info []interface{}]
		// Message: [code byte, data []byte, ping byte]
		// Data: [index[]byte, data []interface{}]
		b := serialize.GetBag()
		if err := b.Unmarshal(packet.ReadBytes()); err != nil {
			b.Release()
			return
		}
		for k, _ := range b.Data {
			row, ok := b.Data[k].([]interface{})
			if !ok {
				continue
			}
			one := serialize.GetBag()
			one.SetList(row)
			hash := one.ReadUint32(0)
			if _, ok := c.BlockMap.Load(hash); ok {
				t := one.ReadByte(1)
				if t&sync_protocol.InfoQueue > 0 {
					// 绑定坐标，用于控制info同步频率
					if c.Target == hash {
						if !one.IsNil(1) {
							tmpLocation := orbit.LocationCM.DecodeLocation(one.ReadBytes(1))
							c.ToUpdateLocation(tmpLocation)
						}
					}
				}
				client := c.CurrentClient
				//fmt.Println("in", one.Data)
				one.Alloc()
				client.Emit().Origin(c).Data(one).Send(sync_protocol.Queue)
				client.Broadcast().Origin(c).Data(one).Send(sync_protocol.Queue)
				//fmt.Println("in2", one.Data)
			}
			one.Release()
		}
		b.Release()
	default:
	}
}

// 关联
func (service *SingleModeService) HandleStreamData(stream socket.StreamRequest, instance interface{}) {
	switch t := instance.(type) {
	case *singleInstance:
		t.DataStream = stream
	default:
		service.Error("Stream Data need single instance")
	}
}
